/*
 * Copyright 2018- The Pixie Authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 * SPDX-License-Identifier: Apache-2.0
 */

syntax = "proto3";

package px.stirling.dynamic_tracing.ir.physical;

import "src/stirling/source_connectors/dynamic_tracer/dynamic_tracing/ir/sharedpb/shared.proto";

// The register field is used to read registers involved in function argument and return tracing.
// For Golang, arguments and return values are passed through the stack, so only SP is relevant.
// For C/C++ with a System V AMD64 ABI calling convention, the other registers are used to pass
// certain arguments and return values. For details see:
//   https://en.wikipedia.org/wiki/X86_calling_conventions#System_V_AMD64_ABI
enum Register {
  // Stack pointer.
  SP = 0;

  // Frame base pointer.
  BP = 1;

  // Direct return value of the System V ABI calling convention.
  // Useful when the return value is already a pointer to the data,
  // which happens whenever the return value is > 16B in size,
  // since the compiler will use a hidden parameter, and return a pointer to the data.
  RC = 100;

  // Pointer to the return value of the System V ABI calling convention.
  // Useful when the return value is 16B or less in size.
  // This will copy the register contents onto the BPF stack and provide a pointer
  // to the data.
  RC_PTR = 101;

  // X86_64 Registers (http://6.s081.scripts.mit.edu/sp18/x86-64-architecture-guide.html).
  RAX = 200;
  RBX = 201;
  RCX = 202;
  RDX = 203;
  RDI = 204;
  RSI = 205;
  R8 = 206;
  R9 = 207;
  R10 = 208;
  R11 = 209;
  R12 = 210;
  R13 = 211;
  R14 = 212;
  R15 = 213;

  // Places argument-passing registers onto the BPF stack as a contiguous piece of memory,
  // and returns the pointer to that memory.
  // System V ABI: https://wiki.osdev.org/System_V_ABI
  // Golang register ABI:
  // https://go.googlesource.com/go/+/refs/heads/dev.regabi/src/cmd/compile/internal-abi.md
  SYSV_AMD64_ARGS_PTR = 300;
  GOLANG_ARGS_PTR = 301;
}

enum VariableStmtOp {
  // Define variable and assign value to it.
  DEFINE_AND_ASSIGN = 0;

  // Only assign value to the variable.
  ASSIGN_ONLY = 1;

  // Only define the variable.
  DEFINE_ONLY = 2;
}

message StructSpecEntry {
  // Location of this struct base type member within the raw bytes.
  int32 offset = 1;

  // Size of this struct base type member.
  int32 size = 2;

  // The name of the base type (e.g. int, float64, etc.), according to DWARF.
  shared.ScalarType type = 3;

  // The path encoding the original data source structure, before flattening.
  // This field uses "JSON pointer" syntax to simplify decoding the data into JSON.
  // See: https://rapidjson.org/md_doc_pointer.html
  string path = 4;
}

message StructSpec {
  // A struct spec is a list of entries of base types in the flattened struct.
  // These are listed in order of memory layout.
  repeated StructSpecEntry entries = 1;
}

message Field {
  string name = 1;
  shared.ScalarType type = 2;

  // Used only if type is STRUCT_BLOB, in which case this provides the spec
  // that defines what the blob contains.
  //
  // If there are more than one element in this field, then the output data is prepended with
  // a single byte 'index' to specify the correct StructSpec for the output data.
  //
  // For example, this is used for Golang interface tracing to specify the actual implementation
  // type of the traced interface.
  repeated StructSpec blob_decoders = 3;
}

// Describes a C struct. This is directly converted to a C struct in BCC code.
message Struct {
  // The name of this struct.
  string name = 1;

  repeated Field fields = 2;
}

// Describes a BPF_PERCPU_ARRAY.
// TODO(yzhao): Consider merging this with Map.
message PerCPUArray {
  // The name of this array.
  string name = 1;

  // The type of the elements of this array.
  shared.VariableType type = 2;

  // The capacity of this array.
  uint32 capacity = 3;
}

// Describes a variable that resides in memory, in comparison to a Register variable.
message MemoryExpression {
  // This must refer to a ScalarVariable with VOID_POINTER type, which specifies the base address
  // to access this variable.
  string base = 1;

  // The offset off the base address.
  int32 offset = 2;

  // Only used if ScalarType is STRUCT_BLOB.
  // Otherwise size is automatically inferred based on the ScalarType during code generation.
  // TODO(oazizi): Remove once STRUCT_BLOB is
  uint32 size = 3;

  // Only used if ScalarType is STRUCT_BLOB.
  // Specify the index of the byte blob's decoder among a list of candidate decoders.
  uint32 decoder_idx = 4;

  // Only used if ScalarType is STRUCT_BLOB.
  //
  // TODO(yzhao): This could be moved into Variable. Then that is better to have variable name and
  // type lifted from the wrapped types (ScalarVariable, StructVariable, ...) into Variable as well.
  VariableStmtOp op = 5;
}

// Describes a variable that is calculated from 2 other previously-defined variables.
message BinaryExpression {
  enum Op {
    // TODO(yzhao): Today only support subtraction for function latency calculated.
    SUB = 0;
  }

  Op op = 1;

  string lhs = 2;
  string rhs = 3;
}

// Describes a variable returned from reading a member of a struct pointer.
message MemberExpression {
  // The name of the StructVariable that contains the field.
  string struct_base = 1;

  // Indicate if the struct_base is a pointer.
  bool is_struct_base_pointer = 2;

  // The builtin to generate the key value to access the map.
  string field = 3;
}

// Describes a basic variable inside BCC code.
// The source of of the variable could be from a number of locations (e.g. memory, register, etc.).
message ScalarVariable {
  // Name must be unique in the scope of a Probe.
  string name = 1;

  // Describes type of the value held by this ScalarVariable.
  //
  // When this is VOID_POINTER, this ScalarVariable describes an intermediate variable used to
  // access a scalar variable.
  shared.ScalarType type = 2;

  // Describes the source expression of the scalar variable.
  oneof src_expr_oneof {
    // Source is a register.
    // NOTE: 'reg' is used to avoid name collision with C++ keyword 'register'.
    Register reg = 3;

    // Source is in memory (either stack or heap).
    MemoryExpression memory = 4;

    // Source is a built-in BPF helper function.
    shared.BPFHelper builtin = 5;

    // Source is a constant.
    string constant = 6;

    // Source is an expression calculated from another 2 variables.
    BinaryExpression binary_expr = 7;

    // Source is a member variable of a struct.
    MemberExpression member = 8;
  }
}

message PtrLenVariable {
  // Name must be unique in the scope of a Probe.
  string name = 1;

  // The type of the variable.
  // TODO(oazizi): May need clean-up after resolving ScalarType relationship to STRING/BYTE_ARRAY.
  shared.ScalarType type = 2;

  // The name of the variable that holds the pointer to the data.
  string ptr_var_name = 3;

  // The name of the variable that indicates the length of the data.
  string len_var_name = 4;
}

// Describes a variable returned from reading a map.
// The variable is a pointer to the type held by the map.
//
// NOTE: MapVariable results into a pointer in BCC code.
message MapVariable {
  // Name must be unique in the scope of a Probe.
  string name = 1;

  // The type of the struct returned by the map.
  string type = 2;

  // The name of the BPF map to retrieve the value.
  string map_name = 3;

  // The builtin to generate the key value to access the map.
  oneof key_oneof {
    // The key is a variable.
    string key_variable_name = 4;

    // For a literal value used for lookup the value.
    string key_value = 5;
  }
}

// Describes a struct variable whose fields are assigned values from other scalar variables.
// A struct variable therefore cannot have nested struct variables.
message StructVariable {
  // Name must be unique in the scope of a Probe.
  string name = 1;

  // The struct that holds all values defined below.
  string type = 2;

  // If this variable is a pointer.
  bool is_pointer = 6;

  // Describe an expression to assign variable_name to the specified field.
  message FieldAssignment {
    string field_name = 1;
    oneof value_oneof {
      string variable_name = 2;
      string value = 3;
    }
  }

  // The names of the variables that are assigned to the fields in the above struct.
  // The variable count must be equal to the field count of the above struct.
  // If unset, the corresponding field is default constructed.
  repeated FieldAssignment field_assignments = 3;

  // TODO(yzhao): This could be moved into Variable. Then that is better to have variable name and
  // type lifted from the wrapped types (ScalarVariable, StructVariable, ...) into Variable as well.
  VariableStmtOp op = 4;

  // If true, this variable is output to the userspace. During codegen, this variable is processed
  // right before the output actions, but is after all other variables.
  //
  // TODO(yzhao): Consider change output action codegen to produce the code that assign variables
  // to this struct's fields.
  bool is_output = 5;
}

// A container of different types of variables. Defines how to access them from inside BCC code.
// Variable names must be unique.
message Variable {
  // TODO(yzhao): Consider move the 'name' field of the messages of the below oneof field.
  // TODO(yzhao): Consider move the 'type' field of the messages of the below oneof field.

  oneof var_oneof {
    ScalarVariable scalar_var = 1;
    StructVariable struct_var = 2;
    MapVariable map_var = 3;
    PtrLenVariable ptr_len_var = 4;
  }
}

// Corresponds to inserting value(s) with key to a map.
// Multiple values are grouped together.
message MapStashAction {
  string map_name = 1;

  // The builtin to generate the key value.
  string key_variable_name = 2;

  // The name of the variable to be inserted into this map.
  string value_variable_name = 3;

  // If set, this decide the condition which this map stash should be executed.
  shared.Condition cond = 4;
}

message PerfBufferOutputAction {
  // The name of the perf buffer.
  string perf_buffer_name = 1;

  // The name of the data buffer array that holds the output variable.
  string data_buffer_array_name = 5;

  // The output Struct to wrap the output variables.
  string output_struct_name = 3;

  // The names of the variables to be submitted to the perf buffer.
  // They were assigned to the fields of a wrapper struct variable.
  // The wrapper struct variable is then submitted to the perf buffer as a whole.
  repeated string variable_names = 4;
}

message MapDeleteAction {
  string map_name = 1;

  // The builtin to generate the key value.
  string key_variable_name = 2;
}

// Describes operations performed inside a if block.
message ConditionalBlock {
  // If set, this decide the condition which this map stash should be executed.
  // This references previously-defined variables.
  shared.Condition cond = 1;

  // All variables defined inside this block.
  repeated Variable vars = 2;

  // If non-empty, produces a return statement that returns the specified value.
  string return_value = 3;
}

// Corresponds to a logical probe.
message Probe {
  // The name of the BCC probe function.
  string name = 1;

  // Where to attach this probe.
  shared.Tracepoint tracepoint = 2;

  // All variables defined inside this probe.
  repeated Variable vars = 3;

  // Multiple if blocks of variables and output actions.
  repeated ConditionalBlock cond_blocks = 10;

  // Inserts key and value into a map.
  //
  // The variable to be inserted into the map must be one of the above vars.
  repeated MapStashAction map_stash_actions = 5;

  // Deletes a BPF map entry.
  repeated MapDeleteAction map_delete_actions = 9;

  // Writes a value to perf buffer.
  repeated PerfBufferOutputAction output_actions = 6;

  // Printk text.
  repeated shared.Printk printks = 7;
}

message PerfBufferOutput {
  string name = 1;

  repeated string fields = 2;

  // Describe the name of the struct that holds the output variables.
  string struct_type = 3;
}

// This describes a complete BPF program.
message Program {
  shared.DeploymentSpec deployment_spec = 1;

  // What language is used by the target binary.
  // TODO(oazizi/yzhao): Remove this. Shouldn't be required at the physical level.
  shared.Language language = 6;

  // Describes the global structs.
  repeated Struct structs = 2;

  // Describes BPF maps.
  repeated shared.Map maps = 3;

  // Describes BPF arrays.
  repeated PerCPUArray arrays = 7;

  // Describes perf buffers.
  repeated PerfBufferOutput outputs = 4;

  // Describes probe functions.
  repeated Probe probes = 5;
}
